import { App, Group, IUI, Path, Rect } from "leafer-ui";
import { Animate } from "@leafer-in/animate";
import { rotateMatrix } from "@/utils/tools.ts";

export type MatrixType = Array<Array<number>>;

class Elsfk {
  private GRID_SIZE = 30; // 块大小
  private LAYOUT_HEIGHT = 600; // 网格高度
  private LAYOUT_WIDTH = 450; // 网格高度
  private ROW_GROUP = "RowGroup"; // 行group className
  private DURATION = 500; // 下落时间间隔
  // 下落块形状
  private SHAPES: MatrixType[] = [
    [
      [1, 0],
      [1, 0],
      [1, 0],
      [1, 0],
    ],
    [
      [1, 1],
      [1, 1],
    ],
    [
      [1, 0],
      [1, 0],
      [1, 1],
    ],
    [
      [1, 1],
      [1, 0],
      [1, 0],
    ],
    [
      [1, 0],
      [1, 1],
      [1, 0],
    ],
    [
      [1, 1, 0],
      [0, 1, 1],
    ],
    [
      [0, 1, 1],
      [1, 1, 0],
    ],
  ];
  // 方块样式
  private RECT_ATTR = {
    tag: "Rect",
    width: this.GRID_SIZE,
    height: this.GRID_SIZE,
    cornerRadius: 5,
    fill: "#fbda41",
    stroke: "yellowgreen",
  };

  private app: App | null = null; // canvas实例
  private current: Group | null = null; // 当前方块实例
  private nextCurrent: Group | null = null; // 下一个方块实例
  private currentShape: MatrixType | null = null; // 当前方块形状
  private nextShape: MatrixType | null = null; // 下一个方块形状
  private currentBoard: MatrixType | null = null; // 游戏区域(虚拟矩阵网格)
  private timerId: any = null; // 下落定时器
  private lastStartTime: number = 0; // 上一次记录时间
  private remainingTime: number = 0; // 暂停剩余时间
  public score: number = 0; // 消除得分
  public gameFlag = false; // 游戏状态

  constructor(view: string) {
    this.init(view);
  }
  public init(view: string) {
    this.initApp(view);
  }
  public start() {
    this.clearBlock();
    this.clearBoard();
    this.score = 0; // 重置得分
    this.gameFlag = true;
    this.genBoard(); // 生成空白数组
    this.genNextShape(); // 先生成一个方块
    this.onAdd(); // 生成第一个方块，开始游戏
  }
  public play() {
    if (!this.timerId) return;
    setTimeout(() => this.startTimer(), this.remainingTime);
  }
  public pause() {
    if (!this.timerId) return;
    this.remainingTime = Date.now() - this.lastStartTime;
    clearInterval(this.timerId);
  }

  private onAdd() {
    if (this.current) return;
    const group = this.nextCurrent!;
    group.x = this.middleX;
    group.y = 0;
    this.current = group;
    this.currentShape = this.nextShape;
    this.app?.tree.add(group);

    this.genNextShape();
    // 如果不够位置生成新下落块 游戏结束
    if (this.isOverlap(this.middleX, 0)) {
      this.onFinish();
      return;
    }

    this.startTimer();
  }
  public onRotate() {
    if (!this.current) return;
    const { x, y, offsetX, offsetY, h } = this.currentAttrs;
    // 判断旋转超出网格
    if (h + x + offsetX > this.LAYOUT_WIDTH) return;
    // 判断碰撞
    const newShape = rotateMatrix(this.currentShape!);
    if (this.isOverlap(x + offsetX, y + offsetY, newShape)) return;

    let i = 0;
    newShape.forEach((row, rowIndex) => {
      row.forEach((col, colIndex) => {
        if (col === 1) {
          this.current!.children[i].x = colIndex * this.GRID_SIZE;
          this.current!.children[i].y = rowIndex * this.GRID_SIZE;
          i++;
        }
      });
    });
    this.currentShape = newShape;
  }
  public onLeft() {
    if (!this.current) return;
    const { x, y, offsetX, offsetY } = this.currentAttrs;
    // 判断到最左侧
    if (x + offsetX <= 0) return;
    // 判断碰撞
    const newOffsetX = offsetX - this.GRID_SIZE;
    if (this.isOverlap(x + newOffsetX, y + offsetY)) return;

    this.current.offsetX = newOffsetX;
  }
  public onRight() {
    if (!this.current) return;
    const { x, y, offsetX, offsetY, w } = this.currentAttrs;
    // 判断到最右侧
    if (x + w + offsetX >= this.LAYOUT_WIDTH) return;
    // 判断碰撞
    const newOffsetX = offsetX + this.GRID_SIZE;
    if (this.isOverlap(x + newOffsetX, y + offsetY)) return;

    this.current.offsetX = newOffsetX;
  }
  public onDown(flag: boolean = false) {
    if (!this.current) return;
    const { x, y, offsetX, offsetY, h } = this.currentAttrs;
    const newOffsetY = offsetY + this.GRID_SIZE;
    // 判断到边界
    if (h + offsetY >= this.LAYOUT_HEIGHT) {
      this.onArriveBottom();
      return;
    }
    // 判断碰撞
    if (this.isOverlap(x + offsetX, y + newOffsetY)) {
      // 碰撞则说明到底了
      this.onArriveBottom();
    } else {
      // 否则继续下落
      this.current.offsetY = newOffsetY;
      !flag && this.onDown(); // flag判断是否在定时器中
    }
  }
  private onArriveBottom() {
    this.mergeBoard(); // 保存虚拟位置
    this.mergeGroup(); // 保存canvas位置
    this.checkLine(); // 检查每行是否放满
    this.clearBlock(); // 清除当前方块
    // 1秒后重新生成新下落块
    setTimeout(() => this.onAdd(), this.DURATION);
  }
  private onFinish() {
    this.gameFlag = false;
    ElMessage.success("GameOver! score:" + this.score);
  }

  private initApp(view: string) {
    this.app = new App({
      view,
      wheel: { disabled: true }, // 禁用滚轮
      move: { disabled: true }, // 禁用拖曳
      ground: { type: "draw" },
      tree: {},
      sky: { type: "draw", usePartRender: false },
    });
    // 网格背景
    const grid = new Path({
      path: this.genGrid(),
      stroke: "#fdb12536",
    });
    this.app.ground.add(grid);
  }
  private mergeBoard() {
    const { x, y, offsetX, offsetY, children } = this.currentAttrs;
    const coordX = (x + offsetX) / this.GRID_SIZE; // x坐标
    const coordY = (y + offsetY) / this.GRID_SIZE; // y坐标
    children.forEach((child) => {
      const blockX = coordX + (child.x || 0) / this.GRID_SIZE;
      const blockY = coordY + (child.y || 0) / this.GRID_SIZE;
      this.currentBoard![blockY][blockX]++;
    });
  }
  private mergeGroup() {
    const { x, y, offsetX, offsetY, children } = this.currentAttrs;
    const groups = this.app!.tree.find(`.${this.ROW_GROUP}`);
    debugger;
    children.forEach((child) => {
      const posX = x + offsetX + (child!.x || 0);
      const posY = y + offsetY + (child!.y || 0);
      const row = posY / this.GRID_SIZE;

      groups[row]?.add(
        new Rect({
          x: posX,
          ...this.RECT_ATTR,
          fill: "#b1fa14",
        })
      );
    });
  }
  private checkLine() {
    const arr: number[] = [];
    this.currentBoard!.forEach((row, rowIndex) => {
      if (row.reduce((sum, cur) => sum + cur, 0) === row.length) {
        arr.push(rowIndex);
      }
    });
    // 删除铺满的行
    arr.forEach((rowIndex) => {
      const groups = this.app!.tree.find(`.${this.ROW_GROUP}`);
      // 删除
      this.currentBoard!.splice(rowIndex, 1);
      this.groupRemove(groups[rowIndex]);
      for (let i = 0; i < rowIndex; i++) {
        this.groupDown(groups[i]);
      }
      // 增加空行
      this.currentBoard!.unshift(
        new Array(this.LAYOUT_WIDTH / this.GRID_SIZE).fill(0)
      );
      this.app!.tree.addAt(
        new Group({ className: this.ROW_GROUP, children: [] }),
        0
      );
    });
    // 加分
    this.score += arr.length * arr.length;
  }
  private clearBlock() {
    clearInterval(this.timerId); // 清除当前自动下落定时器
    this.timerId = null; // 清除当前定时器的id
    this.lastStartTime = 0; // 清除当前定时器的记录时间
    this.remainingTime = 0; // 清除当前定时器的暂停时间
    this.current?.remove(); // 清除canvas下落块图像
    this.current = null; // 重置当前方块实例
    this.currentShape = null; // 重置当前方块形状
  }
  private clearBoard() {
    this.nextCurrent = null; // 重置下一个方块实例
    this.nextShape = null; // 重置下一个方块形状
    this.currentBoard = null; // 重置游戏区域(虚拟矩阵网格)
    this.app?.tree.clear(); // 清除游戏区域(canvas视图)
    this.app?.sky.clear(); // 清除下个形状(canvas视图)
  }

  private isOverlap(x: number, y: number, shape?: MatrixType) {
    let flag = false;
    const coordX = x / this.GRID_SIZE; // x坐标
    const coordY = y / this.GRID_SIZE; // y坐标
    !shape && (shape = this.currentShape!);

    shape.forEach((row, rowIndex) => {
      row.forEach((col, colIndex) => {
        if (
          col === 1 &&
          this.currentBoard![coordY + rowIndex][coordX + colIndex] >= 1
        ) {
          flag = true;
        }
      });
    });
    return flag;
  }
  private startTimer() {
    this.lastStartTime = Date.now();
    this.timerId = setInterval(() => {
      this.lastStartTime = Date.now();
      this.onDown(true);
    }, this.DURATION);
  }
  private groupRemove(group: IUI) {
    const ani = new Animate(
      group,
      { offsetX: this.LAYOUT_WIDTH, opacity: 0 },
      {
        duration: 0.5,
        easing: "ease-out",
      }
    );
    ani.on("completed", () => {
      group.remove();
      ani.destroy();
    });
  }
  private groupDown(group: IUI) {
    const { offsetY = 0 } = group;
    const ani = new Animate(
      group,
      { offsetY: offsetY + this.GRID_SIZE },
      {
        duration: 0.5,
        easing: "linear",
      }
    );
    ani.on("completed", () => {
      ani.destroy();
    });
  }

  private genGrid() {
    let r = "";
    for (let i = 0; i <= this.LAYOUT_WIDTH / this.GRID_SIZE; i++) {
      r += `M${this.GRID_SIZE * i} 0 L${this.GRID_SIZE * i} ${this.LAYOUT_HEIGHT}`;
    }
    for (let i = 0; i <= this.LAYOUT_HEIGHT / this.GRID_SIZE; i++) {
      r += `M0 ${this.GRID_SIZE * i} L${this.LAYOUT_WIDTH} ${this.GRID_SIZE * i}`;
    }
    return r;
  }
  private genBoard() {
    const col = this.LAYOUT_WIDTH / this.GRID_SIZE;
    const row = this.LAYOUT_HEIGHT / this.GRID_SIZE;
    // 生成虚拟数据网格
    this.currentBoard = Array(row)
      .fill(0)
      .map(() => Array(col).fill(0));

    // 生成行group 用于移除整行
    for (let i = 0; i < this.LAYOUT_HEIGHT / this.GRID_SIZE; i++) {
      const row = new Group({
        className: this.ROW_GROUP,
        y: i * this.GRID_SIZE,
        children: [],
      });
      this.app!.tree.add(row);
    }
  }
  private genBlock() {
    const arr = [] as any[];
    this.nextShape!.forEach((row, rowIndex) => {
      row.forEach((col, colIndex) => {
        if (col === 1) {
          arr.push({
            x: colIndex * this.GRID_SIZE,
            y: rowIndex * this.GRID_SIZE,
            ...this.RECT_ATTR,
          });
        }
      });
    });
    return arr;
  }
  private genNextShape() {
    this.app?.sky.remove(this.nextCurrent!);
    this.nextShape = this.randomShape;
    // 下一个形状组
    const nextGroup = new Group({
      x: this.LAYOUT_WIDTH + this.GRID_SIZE,
      y: this.GRID_SIZE,
      children: this.genBlock(),
    });
    this.nextCurrent = nextGroup;
    this.app!.sky.add(nextGroup);
  }

  private get currentAttrs() {
    const current = this.current!;
    const x = current.x || 0;
    const y = current.y || 0;
    const offsetX = current.offsetX || 0;
    const offsetY = current.offsetY || 0;
    const w = current.boxBounds.width || 0;
    const h = current.boxBounds.height || 0;
    const children = current.children || [];
    return { x, y, offsetX, offsetY, w, h, children };
  }
  private get middleX() {
    return Math.floor(this.LAYOUT_WIDTH / this.GRID_SIZE / 2) * this.GRID_SIZE;
  }
  private get randomShape() {
    return this.SHAPES[Math.floor(Math.random() * this.SHAPES.length)];
  }
}

export default Elsfk;
